using System.Collections;
using System;
using SBW;

namespace Athena.SBW
{
	/// <summary>
	/// Summary description for Simulator.
	/// </summary>
	public class SBWSimulator : Plugins.Simulator
	{
		private string _sSBML = "";


        public ArrayList getReactionNames()
        {
            return new ArrayList();
        }

        public string Name
        {
            get { return "SBW Simulator"; }
        }

		public string SBML
		{
			get { return _sSBML; }
			set
			{
				if (value != null && value != "")
					_sSBML = value;
			}
		}

		private string _sModule;

		public string ModuleName
		{
			get { return _sModule; }
			set { _sModule = value; }
		}
		private string _sService;

		public string ServiceName
		{
			get { return _sService; }
			set { _sService = value; }
		}

		private int _nCapabilities;

		public int Capabilities
		{
			get { return _nCapabilities; }
			set { _nCapabilities = value; }
		}

		private Module _oModule = null;
		private Service _oService = null;

		private Method _oLoadSBML = null;
        private Method _oGetSBML = null;
        private Method _oGetValue = null;
        private Method _oSetValue = null;
		private Method _oReset = null;
		private Method _oSetTimeStart = null;
		private Method _oSetTimeEnd = null;
		private Method _oSetNumPoints = null;
		private Method _oSimulate = null;

		private Method _oGetFloatingSpeciesNames = null;
		private Method _oGetFloatingSpeciesValues = null;
		private Method _oGetBoundarySpeciesNames = null;
		private Method _oGetBoundarySpeciesValues = null;
		private Method _oSteady = null;
		private Method _oGetJacobian = null;
		private Method _oSetFloatingSpeciesByIndex = null;
		private Method _oGetFloatingSpeciesByIndex = null;
		private Method _oSetBoundarySpeciesByIndex = null;
		private Method _oOneStep = null;

        private Method _oGetSelectionList = null;
        private Method _oSetSelectionList = null;
        private Method _oGetAvailableSymbols = null;



		private Method _oGetNumberOfReactions = null;
		private Method _oSetLocalParameterByIndex = null;
		private Method _oGetLocalParameterByIndex = null;
		private Method _oGetLocalParameterNames = null;
		private Method _oGetLocalParameterValues = null;
		private Method _oGetGlobalParameterValues = null;
		private Method _oGetGlobalParameterNames = null;
		private Method _oSetGlobalParameterByIndex = null;
		private Method _oGetGlobalParameterByIndex = null;

		private Method _oSetIntegratorCaps = null;
		private Method _oGetIntegratorCaps = null;

		public bool IsConfigurable
		{
			get 
			{
				return (_oSetIntegratorCaps != null && _oGetIntegratorCaps != null);
			}
		}
		public void restartIfNecessary(int nModuleId)
		{
			if (nModuleId == _oModule.ID)
				requestInstance();

		}
		public SBWSimulator(string sModule, string sService)
		{
			_sModule = sModule;
			_sService = sService;
			//requestInstance();
		}

		public void requestInstance()
		{
			try
			{
                if (!LowLevel.SBWGetConnectionStatus())
                    LowLevel.SBWConnect();

				_nCapabilities = 0;
				_oModule = new Module(_sModule);
				_oService = _oModule.getService(_sService);

				_oLoadSBML = _oService.getMethod("void loadSBML(string)");
                try
                {
                    _oGetSBML = _oService.getMethod("string getSBML()");
                }
                catch (Exception)
                {
                    ///
                }
                try
                {
                    _oGetValue = _oService.getMethod("double getValue(string)");
                }
                catch (Exception)
                {
                    ///
                }
                try
                {
                    _oSetValue = _oService.getMethod("void setValue(string,double)");
                }
                catch (Exception)
                {
                    ///
                } 
                
                _oSetTimeStart = _oService.getMethod("void setTimeStart(double)");
				_oSetTimeEnd = _oService.getMethod("void setTimeEnd(double)");
				_oSetNumPoints = _oService.getMethod("void setNumPoints(int)");
				_oSimulate = _oService.getMethod("double[][] simulate()");
				_oReset = _oService.getMethod("void reset()");

				_nCapabilities = 1;
				try
				{
					_oGetIntegratorCaps =_oService.getMethod("string getCapabilities()");
					_oSetIntegratorCaps = _oService.getMethod("void setCapabilities(string)");
					
				}
				catch (Exception)
				{
				}

				try
				{
					_oGetFloatingSpeciesByIndex = _oService.getMethod("double getFloatingSpeciesByIndex (int)");
					_oOneStep = _oService.getMethod("double oneStep(double,double)");
					_nCapabilities = 2;
				}
				catch (Exception)
				{
				}

				try
				{
                    try
                    {
                        _oGetSelectionList = _oService.getMethod("{} getSelectionList()");
                    }
                    catch(Exception)
                    {
                    }
                    try
                    {
                        _oSetSelectionList = _oService.getMethod("void setSelectionList({})");
                    }
                    catch(Exception)
                    {
                    }
                    
                    try
                    {
                        _oGetAvailableSymbols = _oService.getMethod("{} getAvailableSymbols()");

                    }
                    catch(Exception)
                    {
                    }
                    

					try
					{
						_oGetFloatingSpeciesNames = _oService.getMethod("{} getFloatingSpeciesNames()");
					}
					catch (SBWMethodNotFoundException)
					{
						_oGetFloatingSpeciesNames = _oService.getMethod("{} getSpeciesNames()");
					}
					try
					{
						_oGetBoundarySpeciesNames = _oService.getMethod("{} getBoundarySpeciesNames()");
					}
					catch (SBWMethodNotFoundException)
					{
						_oGetBoundarySpeciesNames = _oService.getMethod("{} getBoundarySpeciesList ()");
					} 
					try
					{
						_oGetFloatingSpeciesValues = _oService.getMethod("double[] getFloatingSpeciesConcentrations()");
					}
					catch (SBWMethodNotFoundException)
					{
						_oGetFloatingSpeciesValues = _oService.getMethod("double[] getSpeciesValues()");
					}
					try
					{
						_oGetBoundarySpeciesValues = _oService.getMethod("double[] getBoundarySpeciesConcentrations()");
					}
					catch (SBWMethodNotFoundException)
					{
						_oGetBoundarySpeciesValues = _oService.getMethod("double[] getBoundarySpeciesValues ()");
					}
					
					_oSteady = _oService.getMethod("double steadyState()");
					try
					{
						_oGetJacobian = _oService.getMethod("double[][] getReducedJacobian()");
					}
					catch
					{

						try
						{
							_oGetJacobian = _oService.getMethod("double[][] reducedJacobian()");
						}
						catch (SBWMethodNotFoundException)
						{
							_oGetJacobian = _oService.getMethod("double[][] getJacobian()");
						}
					}

					_oSetFloatingSpeciesByIndex = _oService.getMethod("void setFloatingSpeciesByIndex (int, double)");
					
					_oSetBoundarySpeciesByIndex = _oService.getMethod("void setBoundarySpeciesByIndex (int, double)");



					_nCapabilities = 3;

					try
					{
						_oGetNumberOfReactions = _oService.getMethod("int getNumberOfReactions()");
						_oSetLocalParameterByIndex = _oService.getMethod("void setLocalParameterByIndex(int,int,double)");
						_oGetLocalParameterByIndex = _oService.getMethod("double getLocalParameterByIndex(int,int)");
						_oGetLocalParameterNames = _oService.getMethod("{} getLocalParameterNames(int)");
						_oGetLocalParameterValues = _oService.getMethod("double[] getLocalParameterValues(int)");
						_oGetGlobalParameterValues = _oService.getMethod("double[] getGlobalParameterValues()");
						_oGetGlobalParameterNames = _oService.getMethod("{} getGlobalParameterNames()");
						_oSetGlobalParameterByIndex = _oService.getMethod("void setGlobalParameterByIndex(int,double)");
						_oGetGlobalParameterByIndex = _oService.getMethod("double getGlobalParameterByIndex(int)");

						_nCapabilities = 4;
					}
					catch (SBWMethodNotFoundException)
					{
					}
				}
				catch (SBWMethodNotFoundException)
				{
				}
			}
			catch (Exception)
			{
			}
		}

		public double[,] reducedJacobian()
		{
			if (_oGetJacobian != null)
				return _oGetJacobian.Call().get2DDoubleArray();
			throw new SBWApplicationException("The currently selected simulator does not support this operation");
		}

		public double[,] simulate()
		{
			requestInstance();
			if (_oSimulate != null)
				return _oSimulate.Call().get2DDoubleArray();
			throw new SBWApplicationException("The currently selected simulator does not support this operation");
		}

        public bool HasModel
        {
            get
            {
                return !string.IsNullOrEmpty(getSBML());
            }
        }

        public bool IsLoaded(Core.Module module)
        {
            if (module == null) return true;
            string sModel = Utility.GetSBML(module.JarnacScript);
            return (sModel == getSBML());
        }

        public double getValue(string sId)
        {
            requestInstance();
            if (_oGetValue != null)
            {
                return _oGetValue.Call(sId).getDouble();
            }
            return double.NaN;
        }

        public void setValue(string sId, double dValue)
        {
            requestInstance();
            if (_oSetValue != null)
            {
                _oSetValue.Send(sId, dValue);
            }
        }

        public string getSBML()
        {
            requestInstance();
            if (_oLoadSBML != null)
            {
                _sSBML = _oGetSBML.Call().getString();
            }
            return _sSBML;            

        }
        private static string _sJarnac;
        public static string Jarnac
        {
            get { return _sJarnac; }
            set { _sJarnac = value; }
        }

        Core.Module _CurrentModule = null;
        public Core.Module CurrentModule
        {
            get { return _CurrentModule; }
            set
            {
                if (value != null)
                {
                    try
                    {
                        loadSBML(Utility.GetSBML(value.JarnacScript));
                        _CurrentModule = value;
                    }
                    catch
                    {
                        Utility.SetMessage("Failed to load SBML");
                    }
                }
            }
        }
        public void loadSBML()
        {
            if (!string.IsNullOrEmpty(_sJarnac))
                loadSBML(Utility.GetSBML(_sJarnac));
            else
                loadSBML(Utility.GetSBML(Program.Model,false));
        }
		public void loadSBML(string sModel)
		{
			if (sModel == null || sModel == "") return;
			requestInstance();
			if (_oLoadSBML != null)
			{
				SBML = sModel;

                try { _oLoadSBML.Call(SBML); }
                catch (Exception)
                {
                    //SBML loading error 
                }

			}
			else
			{
				throw new SBWApplicationException("The currently selected simulator does not support this operation");
			}
		}
		public void reset()
		{
            if (_oReset != null)
            {
                _oReset.Call();
                SBML = "";
            }
            else
            {
                throw new SBWApplicationException("The currently selected simulator does not support this operation");
            }
		}
		public void setTimeStart(double dStart)
		{
			if (_oSetTimeStart != null)
				_oSetTimeStart.Call(dStart);
			else
			{
				throw new SBWApplicationException("The currently selected simulator does not support this operation");
			}
		}
		public void setTimeEnd(double dStart)
		{
			if (_oSetTimeEnd != null)
				_oSetTimeEnd.Call(dStart);
			else
			{
				throw new SBWApplicationException("The currently selected simulator does not support this operation");
			}
		}

		public void setNumPoints(int nPoints)
		{
			if (_oSetNumPoints != null)
				_oSetNumPoints.Call(nPoints);
			else
			{
				throw new SBWApplicationException("The currently selected simulator does not support this operation");
			}
		}

		public ArrayList getBoundarySpeciesNames()
		{
			try
			{
				if (_oGetBoundarySpeciesNames != null)
					return _oGetBoundarySpeciesNames.Call().getList();
				else
				{
					Module oModule = new Module("edu.caltech.NOMClipboard");
					oModule.getService("NOM").getMethod("void loadSBML(string)").Call(_sSBML);
					ArrayList oTemp = oModule.getService("NOM").getMethod("{} getListOfBoundarySpecies()").Call().getList();
					ArrayList oResult = new ArrayList();
					foreach (ArrayList l in oTemp)
						oResult.Add((string)l[0]);
					return oResult;
				}
			}
			catch (Exception)
			{
				Module oModule = new Module("edu.caltech.NOMClipboard");
				oModule.getService("NOM").getMethod("void loadSBML(string)").Call(_sSBML);
				ArrayList oTemp = oModule.getService("NOM").getMethod("{} getListOfBoundarySpecies()").Call().getList();
				ArrayList oResult = new ArrayList();
				foreach (ArrayList l in oTemp)
					oResult.Add((string)l[0]);
				return oResult;
			}

		}
		public ArrayList getFloatingSpeciesNames()
		{
			try
			{
				if (_oGetFloatingSpeciesNames != null)
					return _oGetFloatingSpeciesNames.Call().getList();
				else
				{
					Module oModule = new Module("edu.caltech.NOMClipboard");
					oModule.getService("NOM").getMethod("void loadSBML(string)").Call(_sSBML);
					ArrayList oTemp = oModule.getService("NOM").getMethod("{} getListOfFloatingSpecies()").Call().getList();
					ArrayList oResult = new ArrayList();
					foreach (ArrayList l in oTemp)
						oResult.Add((string)l[0]);
					return oResult;
				}
			}
			catch (Exception)
			{
                try
                {
                    Module oModule = new Module("edu.caltech.NOMClipboard");
                    oModule.getService("NOM").getMethod("void loadSBML(string)").Call(_sSBML);
                    ArrayList oTemp = oModule.getService("NOM").getMethod("{} getListOfFloatingSpecies()").Call().getList();
                    ArrayList oResult = new ArrayList();
                    foreach (ArrayList l in oTemp)
                        oResult.Add((string)l[0]);
                    return oResult;
                }
                catch (Exception)
                {
                    return new ArrayList();
                }
			}

		}
		public void setBoundarySpeciesByIndex(int nIndex, double dValue)
		{
			if (_oSetBoundarySpeciesByIndex != null)
			{
				_oSetBoundarySpeciesByIndex.Call(nIndex, dValue);
			}
			else
				throw new SBWApplicationException("The currently selected simulator does not support this operation");

		}
		public void setFloatingSpeciesByIndex(int nIndex, double dValue)
		{
			if (_oSetFloatingSpeciesByIndex != null)
			{
				_oSetFloatingSpeciesByIndex.Call(nIndex, dValue);
			}
			else
				throw new SBWApplicationException("The currently selected simulator does not support this operation");

		}

		public double getFloatingSpeciesByIndex(int nIndex)
		{
			try
			{
				if (_oGetFloatingSpeciesByIndex != null)
					return _oGetFloatingSpeciesByIndex.Call(nIndex).getDouble();
				else
				{
					Module oModule = new Module("edu.caltech.NOMClipboard");
					oModule.getService("NOM").getMethod("void loadSBML(string)").Call(_sSBML);
					ArrayList oTemp = oModule.getService("NOM").getMethod("{} getListOfFloatingSpecies()").Call().getList();
					double[] oValues = new double[oTemp.Count];
					for (int i = 0; i < oTemp.Count; i++)
						oValues[i] = (double)((ArrayList)oTemp[i])[1];
					return oValues[nIndex];
				}
			}
			catch (Exception)
			{
				Module oModule = new Module("edu.caltech.NOMClipboard");
				oModule.getService("NOM").getMethod("void loadSBML(string)").Call(_sSBML);
				ArrayList oTemp = oModule.getService("NOM").getMethod("{} getListOfFloatingSpecies()").Call().getList();
				double[] oValues = new double[oTemp.Count];
				for (int i = 0; i < oTemp.Count; i++)
					oValues[i] = (double)((ArrayList)oTemp[i])[1];
				return oValues[nIndex];
			}
		}

		public double[] getBoundarySpeciesConcentration()
		{
			try
			{
				if (_oGetBoundarySpeciesValues != null)
				{
					return _oGetBoundarySpeciesValues.Call().get1DDoubleArray();
				}
				else
				{
					Module oModule = new Module("edu.caltech.NOMClipboard");
					oModule.getService("NOM").getMethod("void loadSBML(string)").Call(_sSBML);
					ArrayList oTemp = oModule.getService("NOM").getMethod("{} getListOfBoundarySpecies()").Call().getList();
					double[] oValues = new double[oTemp.Count];
					for (int i = 0; i < oTemp.Count; i++)
						oValues[i] = (double)((ArrayList)oTemp[i])[1];
					return oValues;
				}
			}
			catch (Exception)
			{
				Module oModule = new Module("edu.caltech.NOMClipboard");
				oModule.getService("NOM").getMethod("void loadSBML(string)").Call(_sSBML);
				ArrayList oTemp = oModule.getService("NOM").getMethod("{} getListOfBoundarySpecies()").Call().getList();
				double[] oValues = new double[oTemp.Count];
				for (int i = 0; i < oTemp.Count; i++)
					oValues[i] = (double)((ArrayList)oTemp[i])[1];
				return oValues;
			}
		}
		public double[] getFloatingSpeciesConcentration()
		{
			try
			{
				if (_oGetFloatingSpeciesValues != null)
				{
					return _oGetFloatingSpeciesValues.Call().get1DDoubleArray();
				}
				else
				{
					Module oModule = new Module("edu.caltech.NOMClipboard");
					oModule.getService("NOM").getMethod("void loadSBML(string)").Call(_sSBML);
					ArrayList oTemp = oModule.getService("NOM").getMethod("{} getListOfFloatingSpecies()").Call().getList();
					double[] oValues = new double[oTemp.Count];
					for (int i = 0; i < oTemp.Count; i++)
						oValues[i] = (double)((ArrayList)oTemp[i])[1];
					return oValues;
				}
			}
			catch (Exception)
			{
				Module oModule = new Module("edu.caltech.NOMClipboard");
				oModule.getService("NOM").getMethod("void loadSBML(string)").Call(_sSBML);
				ArrayList oTemp = oModule.getService("NOM").getMethod("{} getListOfFloatingSpecies()").Call().getList();
				double[] oValues = new double[oTemp.Count];
				for (int i = 0; i < oTemp.Count; i++)
					oValues[i] = (double)((ArrayList)oTemp[i])[1];
				return oValues;
			}
		}

		public double oneStep(double dStartTime, double dTimeStep)
		{
			if (_oOneStep != null)
				return _oOneStep.Call(dStartTime, dTimeStep).getDouble();
			else
			{
				throw new SBWApplicationException("The currently selected simulator does not support this operation");
			}
		}
		public double steadyState()
		{
			if (_oSteady != null)
				return _oSteady.Call().getDouble();
			else
			{
				throw new SBWApplicationException("The currently selected simulator does not support this operation");
			}
		}



		public void setLocalParameterByIndex(int reactionId, int parameterId, double dValue)
		{
			if (_oSetLocalParameterByIndex != null)
			{
				_oSetLocalParameterByIndex.Call(reactionId, parameterId, dValue);
			}
			else
				throw new SBWApplicationException("The currently selected simulator does not support this operation");
		}
		public double getLocalParameterByIndex(int reactionId, int parameterId)
		{
			if (_oGetLocalParameterByIndex != null)
			{
				return _oGetLocalParameterByIndex.Call(reactionId, parameterId).getDouble();
			}
			else
				throw new SBWApplicationException("The currently selected simulator does not support this operation");
		}
		public double getGlobalParameterByIndex(int parameterId)
		{
			if (_oGetGlobalParameterByIndex != null)
			{
				return _oGetGlobalParameterByIndex.Call(parameterId).getDouble();
			}
			else
				throw new SBWApplicationException("The currently selected simulator does not support this operation");
		}
		public void setGlobalParameterByIndex(int parameterId, double dValue)
		{
			if (_oSetGlobalParameterByIndex != null)
			{
				_oSetGlobalParameterByIndex.Call(parameterId, dValue);
			}
			else
				throw new SBWApplicationException("The currently selected simulator does not support this operation");
		}

		public ArrayList getLocalParameterNames(int reactionId)
		{
			if (_oGetLocalParameterNames != null)
			{
				return _oGetLocalParameterNames.Call(reactionId).getList();
			}
			else
				throw new SBWApplicationException("The currently selected simulator does not support this operation");
		}

		public int getNumberOfReactions()
		{
			if (_oGetNumberOfReactions != null)
			{
				return _oGetNumberOfReactions.Call().getInt();
			}
			else
				throw new SBWApplicationException("The currently selected simulator does not support this operation");
		}

		public ArrayList getGlobalParameterNames()
		{
			if (_oGetGlobalParameterNames != null)
			{
				return _oGetGlobalParameterNames.Call().getList();
			}
			else
				throw new SBWApplicationException("The currently selected simulator does not support this operation");
		}
		public double[] getGlobalParameterValues()
		{
			if (_oGetGlobalParameterValues != null)
			{
				return _oGetGlobalParameterValues.Call().get1DDoubleArray();
			}
			else
				throw new SBWApplicationException("The currently selected simulator does not support this operation");
		}

        public ArrayList getSelectionList()
        {
            if (_oGetSelectionList != null)
            {
                return _oGetSelectionList.Call().getList();
            }
            else
            {
                ArrayList oResult = new ArrayList();
                oResult.Add("time");
                oResult.AddRange(getFloatingSpeciesNames());
                return oResult;
            }
        }


        public ArrayList getAvailableSymbols()
        {
            if (_oGetAvailableSymbols != null)
            {
                return _oGetAvailableSymbols.Call().getList();
            }
            else
            {
                ArrayList oResult = new ArrayList();
                oResult.Add( new ArrayList ( new object[] { "Time", new ArrayList ( new object[] { "time" } ) } ));

                oResult.Add( new ArrayList ( new object[] { "Floating Species", getFloatingSpeciesNames() }));
                return oResult;
            }
        }

        public void setSelectionList(ArrayList oList)
        {
            if (_oSetSelectionList != null)
            {
                _oSetSelectionList.Call(oList);
            }
            else
            {
                //
            }
        }


		public double[] getLocalParameterValues(int reactionId)
		{
			if (_oGetLocalParameterValues != null)
			{
				return _oGetLocalParameterValues.Call(reactionId).get1DDoubleArray();
			}
			else
				throw new SBWApplicationException("The currently selected simulator does not support this operation");
		}

		public string getIntegratorCaps()
		{
			if (IsConfigurable)
			{
				return _oGetIntegratorCaps.Call().getString();
			}
			throw new SBWApplicationException("Simulator is not configurable");
		}

		public void setIntegratorCaps(string sXML)
		{
			if (IsConfigurable)
			{
				_oSetIntegratorCaps.Call(sXML);
			}
		}
	}
}
